/*
 * File: Gas.m
 * Copyright 2008 . All Rights Reserved.
 */

#import "Gas.h"
#import "FuelLogAppDelegate.h"

static sqlite3_stmt *insert_statement = nil;
static sqlite3_stmt *init_statement = nil;
static sqlite3_stmt *delete_statement = nil;
static sqlite3_stmt *delete_for_car_statement = nil;
static sqlite3_stmt *update_statement = nil;
static sqlite3_stmt *getall_statement = nil;

@implementation Gas

@synthesize dirty;

// Finalize (delete) all of the SQLite compiled queries.
+ (void)finalizeStatements {
    if (insert_statement) sqlite3_finalize(insert_statement);
    if (init_statement) sqlite3_finalize(init_statement);
    if (delete_statement) sqlite3_finalize(delete_statement);
    if (delete_for_car_statement) sqlite3_finalize(delete_for_car_statement);
    if (update_statement) sqlite3_finalize(update_statement);
	if (getall_statement) sqlite3_finalize(getall_statement);
	insert_statement = nil;
	init_statement = nil;
	delete_statement = nil;
	delete_for_car_statement = nil;
	update_statement = nil;
	getall_statement = nil;
}

+ (void)getAll:(NSMutableArray *)output car_id:(int)fk database:(sqlite3 *)db {
	if (getall_statement == nil) {
		const char *sql = "SELECT pk,date,odometer,volume,cost,full FROM gas WHERE car_fk=? ORDER BY odometer desc,date desc";
		if (sqlite3_prepare_v2(db, sql, -1, &getall_statement, NULL) != SQLITE_OK) {
			NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(db));
		}
	}
	sqlite3_bind_int(getall_statement, 1, fk);
	while (sqlite3_step(getall_statement) == SQLITE_ROW) {
		Gas *gas = [Gas alloc];
		[gas setDatabase:db];
		[gas setPk:sqlite3_column_int(getall_statement, 0)];
		[gas setCar_fk:fk];
		[gas setDate:[NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(getall_statement, 1)]];
		[gas setOdometer:(float) sqlite3_column_double(getall_statement, 2)];
		[gas setVolume:(float) sqlite3_column_double(getall_statement, 3)];
		[gas setCost:(float) sqlite3_column_double(getall_statement, 4)];
		[gas setFull:(sqlite3_column_int(getall_statement, 5) > 0 ? YES : NO)];
		[gas setDirty:NO];
		[output addObject:gas];
		[gas release];
	}
	sqlite3_reset(getall_statement);
}

- (id)init:(Car *)car {
	if (self = [super init]) {
		[self setDate:[FuelLogAppDelegate getNoonUTC:[NSDate date]]];
		car_fk = car.pk;
		full = YES;	
		odometer = 0.0;
		volume = 0.0;
		cost = 0.0;
		pk = -1;
	}
	return self;
}

// Creates the object from db
- (id)initWithPrimaryKey:(int)key car_id:(int)fk database:(sqlite3 *)db {
    if (self = [super init]) {
        pk = key;
        database = db;
        if (init_statement == nil) {
            const char *sql = "SELECT car_fk,date,odometer,volume,cost,full FROM cars WHERE pk=?";
            if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
                NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
            }
        }
        sqlite3_bind_int(init_statement, 1, pk);
        if (sqlite3_step(init_statement) == SQLITE_ROW) {
            self.car_fk = sqlite3_column_int(init_statement, 0);
			self.date = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(init_statement, 1)];
			self.odometer = (float) sqlite3_column_double(init_statement, 2);
			self.volume = (float) sqlite3_column_double(init_statement, 3);
			self.cost = (float) sqlite3_column_double(init_statement, 4);
			self.full = (sqlite3_column_int(init_statement, 5) > 0 ? YES : NO);
        } else {
            self.car_fk = fk;
			self.date = [NSDate date];
			self.odometer = 0.0;
			self.volume = 0.0;
			self.cost = 0.0;
			self.full = YES;
        }
        sqlite3_reset(init_statement);
        dirty = NO;
    }
    return self;
}

- (void)insertIntoDatabase:(sqlite3 *)db {
    database = db;
    if (insert_statement == nil) {
        static char *sql = "INSERT INTO gas (car_fk,date,odometer,volume,cost,full) VALUES(?,?,?,?,?,?)";
        if (sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
        }
    }
	sqlite3_bind_int(insert_statement, 1, car_fk);
	sqlite3_bind_double(insert_statement, 2, [date timeIntervalSince1970]);
	sqlite3_bind_double(insert_statement, 3, (double) odometer);
	sqlite3_bind_double(insert_statement, 4, (double) volume);
	sqlite3_bind_double(insert_statement, 5, (double) cost);
	sqlite3_bind_int(insert_statement, 6, (full ? 1 : 0));
    int success = sqlite3_step(insert_statement);
    sqlite3_reset(insert_statement);
    if (success == SQLITE_ERROR) {
        NSAssert1(0, @"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(database));
    } else {
		dirty = NO;
        pk = sqlite3_last_insert_rowid(database);
    }
}

- (void)dealloc {
    [date release];
	date = nil;
    [super dealloc];
}

+ (void)deleteForCar:(sqlite3 *)database car_id:(int)car_fk {
    if (delete_for_car_statement == nil) {
        const char *sql = "DELETE FROM gas WHERE car_fk=?";
        if (sqlite3_prepare_v2(database, sql, -1, &delete_for_car_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
        }
    }
    sqlite3_bind_int(delete_for_car_statement, 1, car_fk);
    int success = sqlite3_step(delete_for_car_statement);
    sqlite3_reset(delete_for_car_statement);
    if (success != SQLITE_DONE) {
        NSAssert1(0, @"Error: failed to delete from database with message '%s'.", sqlite3_errmsg(database));
    }
}

- (void)deleteFromDatabase {
    if (delete_statement == nil) {
        const char *sql = "DELETE FROM gas WHERE pk=?";
        if (sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
        }
    }
    sqlite3_bind_int(delete_statement, 1, pk);
    int success = sqlite3_step(delete_statement);
    sqlite3_reset(delete_statement);
    if (success != SQLITE_DONE) {
        NSAssert1(0, @"Error: failed to delete from database with message '%s'.", sqlite3_errmsg(database));
    }
}

// Save to the database.
- (void)updateDatabase {
    if (dirty) {
        if (update_statement == nil) {
            const char *sql = "UPDATE gas SET date=?,odometer=?,volume=?,cost=?,full=? WHERE pk=?";
            if (sqlite3_prepare_v2(database, sql, -1, &update_statement, NULL) != SQLITE_OK) {
                NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
            }
        }
		sqlite3_bind_double(update_statement, 1, [date timeIntervalSince1970]);
		sqlite3_bind_double(update_statement, 2, (double) odometer);
		sqlite3_bind_double(update_statement, 3, (double) volume);
		sqlite3_bind_double(update_statement, 4, (double) cost);
		sqlite3_bind_int(update_statement, 5, (full ? 1 : 0));
        sqlite3_bind_int(update_statement, 6, pk);
        int success = sqlite3_step(update_statement);
        sqlite3_reset(update_statement);
        if (success != SQLITE_DONE) {
            NSAssert1(0, @"Error: failed to update with message '%s'.", sqlite3_errmsg(database));
        }
        dirty = NO;
    }
}

+ (int) exportHeaderLen {
	return 31;
}

+ (const char *) exportHeader {
	return "date,odometer,volume,cost,full\n";
}

- (const char *) exportCString:(NSDateFormatter *)dateFormatter {
	NSString *d = [dateFormatter stringFromDate:date];
	NSString *s = [NSString stringWithFormat:@"%@,%.3f,%.3f,%.3f,%@\n", d, odometer, volume, cost, (full ? @"Y": @"N")];
	return [s cStringUsingEncoding:NSASCIIStringEncoding];
}

#pragma mark Properties

// do not synthesize because we want to set dirty flag
- (sqlite3 *)database {
	return database;
}

// use only when deserializing all cars from database
- (void)setDatabase:(sqlite3 *)db {
	database = db;
	dirty = NO;
}

- (int)pk {
    return pk;
}

// use only when deserializing all entries from database
- (void)setPk:(int)key {
	if (pk == key) return;
	dirty = NO;
	pk = key;
}

- (int)car_fk {
	return car_fk;
}

- (void)setCar_fk:(int)fk {
	if (car_fk == fk) return;
	dirty = NO;
	car_fk = fk;
}

- (NSDate *)date {
    return date;
}

- (void)setDate:(NSDate *)aDate {
    if ((!date && !aDate) || (date && aDate && [date isEqual:aDate])) return;
    dirty = YES;
    [date release];
    date = [aDate copy];
}

- (float)odometer {
	return odometer;
}

- (void)setOdometer:(float)num {
	if (odometer == num) return;
	dirty = YES;
	odometer = num;
}

- (float)volume {
	return volume;
}

- (void)setVolume:(float)num {
	if (volume == num) return;
	dirty = YES;
	volume = num;
}

- (float)cost {
	return cost;
}

- (void)setCost:(float)num {
	if (cost == num) return;
	dirty = YES;
	cost = num;
}

- (BOOL)full {
	return full;
}

- (void)setFull:(BOOL)b {
	if (full == b) return;
	dirty = YES;
	full = b;
}

@end
